package com.github.chirspan.xaas.security.component;

import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import com.github.chirspan.xaas.security.util.SecurityUtils;
import com.github.chirspan.xaas.rbac.config.SecurityProperties;
import com.github.chirspan.xaas.rbac.dao.RbacResourceDao;
import com.github.chirspan.xaas.rbac.model.RbacResource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.stereotype.Service;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.CollectionUtils;

import javax.servlet.http.HttpServletRequest;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * @author ChenPan
 * @date 2018/6/27 10:24
 * description:
 */
@Slf4j
@Service("permissionService")
public class PermissionService {

    private AntPathMatcher antPathMatcher = new AntPathMatcher();

    @Autowired
    private RbacResourceDao resourceDao;

    @Autowired
    private SecurityProperties securityProperties;

    @Cacheable(value = "urlPermission", key = "#p1.name+'_'+#p0.requestURI+'_'+#p0.method",
            condition = "#p1.name!='anonymousUser'")
    public boolean hasPermission(HttpServletRequest request, Authentication authentication) {
        return checkPermission(authentication, request.getRequestURI(), request.getMethod());
    }


    @Cacheable(value = "urlPermission", key = "#p0.name+'_'+#p1+'_'+#p2",
            condition = "#p0.name!='anonymousUser'")
    public boolean hasPermission(Authentication authentication, String requestUri, String requestMethod) {
        return checkPermission(authentication, requestUri, requestMethod);
    }

    private boolean checkPermission(Authentication authentication, String requestUri, String requestMethod) {
        Object principal = authentication.getPrincipal();
        if (principal == null) {
            return false;
        }
        if (SecurityUtils.hasRole(securityProperties.getRoleSuperAdmin())
                && securityProperties.getAdminHasAllPermission()) {
            return true;
        }
        boolean hasPermission = false;
        if (authentication instanceof AnonymousAuthenticationToken) {
            return false;
        }
        if (MapUtil.isNotEmpty(securityProperties.getUserPermitUrl())) {
            for (Map.Entry<String, String> m : securityProperties.getUserPermitUrl().entrySet()) {
                boolean ignore = antPathMatcher.match(m.getKey(), requestUri)
                        && (m.getValue().contains(requestMethod) || m.getValue().contains("*"));
                if (ignore) {
                    return true;
                }
            }
        }

        List<SimpleGrantedAuthority> grantedAuthorityList = (List<SimpleGrantedAuthority>) authentication.getAuthorities();

        if (principal != null) {
            if (CollectionUtils.isEmpty(grantedAuthorityList)) {
                log.warn("角色列表为空：{}", authentication.getPrincipal());
                return false;
            }
            Set<RbacResource> resources = new HashSet<>();
            for (SimpleGrantedAuthority authority : grantedAuthorityList) {
                resources.addAll(resourceDao.findResourcesByRole(authority.getAuthority(), securityProperties.getRequestResourceType()));
            }
            for (RbacResource resource : resources) {
                if (StrUtil.isNotEmpty(resource.getResourceUrl()) && antPathMatcher
                        .match(resource.getResourceUrl(), requestUri)
                        && requestMethod.equalsIgnoreCase(resource.getRequestMethod())) {
                    hasPermission = true;
                    break;
                }
            }
        }
        return hasPermission;
    }
}
